; myos boot asm
; TAB=4

VBEMODE	EQU		0x103			; 800 x  600 x 8bit彩色
; （画面模式号码）
;	0x100 :  640 x  400 x 8bit彩色
;	0x101 :  640 x  480 x 8bit彩色
;	0x103 :  800 x  600 x 8bit彩色
;	0x105 : 1024 x  768 x 8bit彩色
;	0x107 : 1280 x 1024 x 8bit彩色

BOTPAK	EQU		0x00280000		; bootpack载入地址
DSKCAC	EQU		0x00100000		; 磁盘缓存
DSKCAC0	EQU		0x00008000		; 磁盘缓存（实模式）

; 有关BOOT_INFO
CYLS	EQU		0x0ff0			; 设定启动区
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 关于颜色数目的信息。颜色的位数
SCRNX	EQU		0x0ff4			; 分辨率的X（screen X）
SCRNY	EQU		0x0ff6			; 分辨率的Y（screen Y）
VRAM	EQU		0x0ff8			; 图像缓冲区的开始地址

		ORG		0xc400			; 程序装载到内存的起始地址

; 确认VBE是否存在

		MOV		AX,0x9000
		MOV		ES,AX
		MOV		DI,0
		MOV		AX,0x4f00
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320

; 检查VBE的版本

		MOV		AX,[ES:DI+4]
		CMP		AX,0x0200
		JB		scrn320			; if (AX < 0x0200) goto scrn320

; 取得画面模式信息

		MOV		CX,VBEMODE
		MOV		AX,0x4f01
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320

; 确认画面模式信息

		CMP		BYTE [ES:DI+0x19],8
		JNE		scrn320
		CMP		BYTE [ES:DI+0x1b],4
		JNE		scrn320
		MOV		AX,[ES:DI+0x00]
		AND		AX,0x0080
		JZ		scrn320			; 模式属性的bit7是0，所以放弃

; 切换画面模式

		MOV		BX,VBEMODE+0x4000
		MOV		AX,0x4f02
		INT		0x10
		MOV		BYTE [VMODE],8	; 记下画面模式（参考C语言）
		MOV		AX,[ES:DI+0x12]
		MOV		[SCRNX],AX
		MOV		AX,[ES:DI+0x14]
		MOV		[SCRNY],AX
		MOV		EAX,[ES:DI+0x28]
		MOV		[VRAM],EAX
		JMP		keystatus

scrn320:
		MOV		AL,0x13			; VGA图、320x200x8bit彩色
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 记下画面模式（参考C语言）
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; 用BIOS取得键盘上各种LED指示灯的状态
keystatus:
		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

; PIC关闭一切中断
; 根据AT兼容机的规格，如果要初始化PIC
; 必须在CLI之前进行，否则有时会挂起
; 随后进行PIC的初始化

		MOV		AL,0xff
		OUT		0x21,AL
		NOP						; 如果连续执行OUT指令，有些机种会无法正常运行
		OUT		0xa1,AL

		CLI						; 禁止CPU级别的中断

; 为了让CPU能够访问1MB以上的内存空间，设定A20GATE

		CALL	waitkbdout
		MOV		AL,0xd1
		OUT		0x64,AL
		CALL	waitkbdout
		MOV		AL,0xdf			; 打开A20
		OUT		0x60,AL
		CALL	waitkbdout		; 键盘控制器的附属端口，用于打开A20地址线

;[INSTRSET "i486p"]，虽然是16位指令，但可以使用32位寄存器之类的。
	LGDT 	[GDTR0]
	MOV	EAX, CR0
	AND	EAX, 0x7fffffff
	OR	EAX, 0x00000001
	MOV	CR0, EAX	; 进入保护模式，保护模式的特性只是在相应的地方起作用，如跳转时的检测，分页的检测等，此时由于没有改变CS的值，CPU使用的基地址没有改变（基地址只在改变CS的时读取选择子然后查找描述符时改变），所以程序继续执行，几乎和刚刚的实地址模式没有区别。
	JMP	pipelineflush
pipelineflush:
	MOV	AX, 1*8		; 段选择子，16位。高13位代表序号，然后TI，RPL
	MOV	DS, AX
	MOV	ES, AX
	MOV	FS, AX
	MOV	GS, AX
	MOV	SS, AX		; 数据段，堆栈段等使用同一个段。已进入保护模式，加载段寄存器时，可见部分和不可见部分都会修改。

; 把0x7c00处即MBR或第一个扇区代码拷贝到DSKCAC=0x00100000处
	MOV	ESI, 0x7c00
	MOV	EDI, DSKCAC
	MOV	ECX, 512/4
	CALL	memcpy

; 把从0x8200即第二个扇区开始处代码拷贝到DSKCAC+0x200处，共18＊2 - 1个扇区，所以前18个柱面现在在DSKCAC=0x00100000处。
	MOV	ESI, DSKCAC0+512
	MOV	EDI, DSKCAC+512
	MOV	ECX, 0
	MOV	CL, BYTE[CYLS]
	IMUL	ECX, 512*18*2/4
	SUB	ECX,512/4
	CALL	memcpy

; 32位代码拷贝到BOTPAK=0x00280000处，按elf格式展开
;	PUSH	BOTPAK
	PUSH	bootpack
	CALL	initharibsys

;	MOV	EBX, BOTPAK
;	MOV	ECX, [EBX+16]
;  	ADD	ECX, 3
;	SHR	ECX, 2
;	JZ	skip
;	MOV	ESI, [EBX+20]
;	ADD	ESI, EBX
;	MOV	EDI, [EBX+12]
;	CALL	memcpy
skip:
;	MOV	ESP, [EBX+12]	;
	JMP	DWORD 2*8:0x00001000 ; 改变CS的值，基地址也随之改变，剥离出文件格式信息时失败，还是使用入口偏移，此ELF功能简单，偏移几乎不变，也不需要映射段和重定位。

waitkbdout:
	IN	AL, 0x64
	AND	AL, 0x02
	JNZ	waitkbdout
	RET

memcpy:
	MOV	EAX, [ESI]
	ADD	ESI, 4
	MOV	[EDI], EAX
	ADD	EDI, 4
	SUB	ECX, 1
	JNZ	memcpy
	RET

GDT0:
	TIMES 8 DB 0
	DW	0xffff, 0x0000, 0x9200, 0x00cf
	DW	0xffff, 0x0000, 0x9a28, 0x0047

	DW	0
GDTR0:
	DW	8*3-1
	DD	GDT0

initharibsys:
	PUSH 	EBP
	MOV	EBP, ESP
	MOV	AX, [EBP+0x6] ; [EBP+0x6] elf文件所在的内存地址
	MOVZX	EAX, AX
	MOV	CX, [EAX+0x2c] ; e_phnum;
	MOVZX	ECX, CX
	MOV	EBX, [EAX+0x1c]	; e_phoff
	ADD	EBX, EAX	; EBX->ph
COPYSEC:
	CMP	ECX, 0
	JE	ret
	MOV	ESI, [EBX+0x4]
	ADD	ESI, EAX
	MOV	EDI, [EBX+0x8]
	PUSH	ECX
	MOV	ECX, [EBX+0x10]
	ADD	ECX, 3
	SHR	ECX, 2
	CMP	ECX, 0
	JE	ret0
	PUSH	EAX
	CALL	memcpy
	POP	EAX
	POP	ECX
	SUB	ECX, 1
	MOV	DX, [EAX+0x2a]
	MOVZX	EDX, DX
	ADD	EBX, EDX
	JMP	COPYSEC
ret0:
	POP	ECX
ret:
	MOV	ESI, [EAX+0x18]	; 入口地址
	SUB	ESI, BOTPAK	; 减去段基地址，得到在段中的偏移，跳转使用
;	PUSH	2*8
;	PUSH	SI
;	RETF			; 通过RETF指令修改CS：IP
;	JMP	2*8:ESI		; JMP far 修改CS：IP，但不支持变量，如ESI
; 	JMP	DWORD 2*8:0x1234	; 没有问题
	MOV 	DWORD [offset], ESI 	; 使用嵌入式，硬编码
	DB 0x66
	DB 0xea
offset:
	DD 0
selector:
	DW 2*8
	RET

bootpack:
